Skip to content

Preserve unknown top-level YAML keys and fix metadata loss through import/export cycle#8

Merged
intel352 merged 7 commits intomasterfrom
copilot/fix-yaml-reformatting-issue
Apr 8, 2026
Merged

Preserve unknown top-level YAML keys and fix metadata loss through import/export cycle#8
intel352 merged 7 commits intomasterfrom
copilot/fix-yaml-reformatting-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 8, 2026

Opening a workflow YAML file caused silent destructive changes: unknown top-level keys (e.g. engine:) were deleted, triggers: {} was injected, top-level key ordering was scrambled, and parseYamlSafe silently dropped imports/requires/platform/infrastructure/sidecars.

Root causes

  • parseYaml/parseYamlSafe only handled known fields — anything else was discarded
  • exportToConfig() called nodesToConfig() without originalConfig, bypassing the "omit if not originally present" guard for triggers/workflows/modules
  • configToYaml used a hardcoded key order, ignoring the original file ordering
  • parseYamlSafe was inconsistent with parseYaml (missing 5 passthrough fields)
  • importFromConfig stored only importedWorkflows/Triggers/Pipelines, losing all other original metadata
  • buildMainFileContent (multi-file export path) built output manually, bypassing _extraTopLevelKeys emission and _originalKeys ordering
  • clearCanvas() did not reset importedPassthrough, allowing stale metadata to leak into subsequent exports

Changes

src/types/workflow.ts

  • Added _extraTopLevelKeys?: Record<string, unknown> to WorkflowConfig to carry unrecognised top-level keys through the full parse → store → serialize cycle

src/utils/serialization.ts

  • Added KNOWN_TOP_LEVEL_KEYS set (including application for the legacy multi-file format) and extractExtraTopLevelKeys() helper
  • parseYaml + parseYamlSafe: capture unknown top-level keys into _extraTopLevelKeys
  • parseYamlSafe: fixed to match parseYaml for imports, requires, platform, infrastructure, sidecars
  • nodesToConfig: pass through _extraTopLevelKeys from originalConfig
  • configToYaml: emit _extraTopLevelKeys; honour _originalKeys ordering when serialising (falls back to documented fixed order when _originalKeys absent)
  • resolveImports: propagate _originalKeys and _extraTopLevelKeys from the main file
  • buildMainFileContent: now delegates to configToYaml so multi-file main-file exports also honour _originalKeys ordering and emit _extraTopLevelKeys; ensures modules is always present in the output for multi-file main files (even when all modules live in sub-files)

src/stores/workflowStore.ts

  • Added importedPassthrough field (type excludes pipelines, which is tracked separately via importedPipelines) storing all non-visual config metadata (name, version, _originalKeys, _extraTopLevelKeys, imports, requires, platform, infrastructure, sidecars)
  • importFromConfig: populates importedPassthrough via destructuring
  • exportToConfig: passes importedPassthrough as originalConfig to nodesToConfig, restoring correct _originalKeys so the empty-section omission logic fires correctly and unknown keys are re-emitted
  • clearCanvas: now resets importedPassthrough to null to prevent stale metadata from a previous import leaking into subsequent exports

Example — before/after

Input YAML:

name: my-service
version: "1.0"
engine:
  validation:
    templateRefs: warn   # ← was silently deleted
modules:
  - name: server
    type: http.server
pipelines:
  health: { ... }
  # No `triggers:` key in source

Before: engine: block deleted, triggers: {} injected, key order changed.
After: engine: preserved, triggers absent, name → engine → modules → pipelines order maintained. This fidelity now also applies to multi-file configs exported via buildMainFileContent.

Copilot AI self-assigned this Apr 8, 2026
Copilot AI review requested due to automatic review settings April 8, 2026 13:21
Copilot AI review requested due to automatic review settings April 8, 2026 13:21
Copilot AI linked an issue Apr 8, 2026 that may be closed by this pull request
…injection, fix parseYamlSafe consistency

Agent-Logs-Url: https://github.com/GoCodeAlone/workflow-editor/sessions/fdecd5e6-5e99-4497-be49-ba1ed4033a96

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot April 8, 2026 13:33
…default key ordering, simplified passthrough extraction

Agent-Logs-Url: https://github.com/GoCodeAlone/workflow-editor/sessions/fdecd5e6-5e99-4497-be49-ba1ed4033a96

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot April 8, 2026 13:39
…se clearer destructuring names

Agent-Logs-Url: https://github.com/GoCodeAlone/workflow-editor/sessions/fdecd5e6-5e99-4497-be49-ba1ed4033a96

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot April 8, 2026 13:41
Copilot AI changed the title [WIP] Fix editor causing destructive changes to YAML files Preserve unknown top-level YAML keys and fix metadata loss through import/export cycle Apr 8, 2026
Copilot AI requested a review from intel352 April 8, 2026 13:42
@intel352 intel352 marked this pull request as ready for review April 8, 2026 13:43
Copilot AI review requested due to automatic review settings April 8, 2026 13:43
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves YAML round-tripping fidelity for workflow configs by preserving unknown top-level keys and restoring original top-level key ordering across parse → edit → export, while also fixing parseYamlSafe field loss and preventing unintended empty-section injection.

Changes:

  • Add _extraTopLevelKeys to WorkflowConfig and propagate it through parsing, node export, and YAML serialization.
  • Update configToYaml to honor _originalKeys ordering (with a default fallback order) and to emit preserved unknown keys.
  • Extend store import/export to persist non-visual metadata (importedPassthrough) so export can reconstruct a complete config consistently.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
src/utils/serialization.ts Preserve unknown top-level YAML keys, keep original top-level key order in configToYaml, and align parseYamlSafe field passthrough with parseYaml.
src/utils/serialization-bugs.test.ts Add regression tests for unknown-key preservation, triggers injection avoidance, and parseYamlSafe field consistency.
src/types/workflow.ts Extend WorkflowConfig with _extraTopLevelKeys for unknown top-level YAML preservation.
src/stores/workflowStore.ts Persist imported non-visual metadata and pass it back into nodesToConfig to prevent metadata loss during export.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1271 to +1276
// Preserve top-level key ordering and unknown keys from the main file
config._originalKeys = Object.keys(parsed);
const extraTopLevelKeys = extractExtraTopLevelKeys(parsed);
if (extraTopLevelKeys) {
config._extraTopLevelKeys = extraTopLevelKeys;
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolveImports() now captures _originalKeys and _extraTopLevelKeys from the main file, but the multi-file export path (exportToFiles/exportMainFileYaml via buildMainFileContent) does not use configToYaml and currently does not emit unknown top-level keys or preserve the original top-level key ordering. As a result, multi-file round-trips will still drop custom blocks (e.g. engine:) and reorder keys in the main file. Consider updating buildMainFileContent to incorporate _extraTopLevelKeys and honor _originalKeys (or delegate to configToYaml for the main-file content).

Copilot uses AI. Check for mistakes.
Comment on lines +465 to +473
const { nodes, edges, importedWorkflows, importedTriggers, importedPipelines, importedPassthrough } = get();
const moduleTypeMap = useModuleSchemaStore.getState().moduleTypeMap;
const config = nodesToConfig(nodes, edges, moduleTypeMap);
// Pass importedPassthrough as originalConfig so that nodesToConfig can restore
// non-visual fields: name, version, _originalKeys, _extraTopLevelKeys, imports,
// requires, platform, infrastructure, sidecars.
const originalConfig: import('../types/workflow.ts').WorkflowConfig | undefined = importedPassthrough
? { modules: [], workflows: {}, triggers: {}, ...importedPassthrough }
: undefined;
const config = nodesToConfig(nodes, edges, moduleTypeMap, originalConfig);
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exportToConfig() always reuses importedPassthrough when present, but clearCanvas() does not reset importedPassthrough. This can cause stale name/version/_originalKeys/_extraTopLevelKeys/etc. from a previously imported YAML to leak into subsequent exports after clearing the canvas. Clear importedPassthrough (and potentially sourceMap) in clearCanvas(), or gate passthrough usage on whether a config was actually imported for the current session.

Copilot uses AI. Check for mistakes.
@intel352
Copy link
Copy Markdown
Contributor

intel352 commented Apr 8, 2026

@copilot apply changes based on the comments in this thread

Copilot AI and others added 2 commits April 8, 2026 14:16
…pipelines from passthrough type, fix buildMainFileContent for multi-file key ordering and unknown keys

Agent-Logs-Url: https://github.com/GoCodeAlone/workflow-editor/sessions/cbe50c21-b5cc-4238-ab7b-36cb53bc6e40

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot April 8, 2026 14:19
Copilot AI requested review from Copilot and removed request for Copilot April 8, 2026 14:21
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 8, 2026

@copilot apply changes based on the comments in this thread

Applied all three changes from the review thread (commit 6c5ce0e):

  1. buildMainFileContent multi-file round-trip — now delegates to configToYaml, so _extraTopLevelKeys are emitted and _originalKeys ordering is honoured. Also added application to KNOWN_TOP_LEVEL_KEYS (so the legacy application: format key is not captured as an unknown extra) and ensures modules is always in adjustedOriginalKeys for multi-file main files so modules: [] is always emitted even when all modules live in sub-files.

  2. clearCanvas() stale metadataclearCanvas() now resets importedPassthrough: null alongside the other fields.

  3. importedPassthrough type includes pipelines — type updated to Omit<WorkflowConfig, 'modules' | 'workflows' | 'triggers' | 'pipelines'> since pipelines are tracked separately via importedPipelines.

@intel352 intel352 merged commit 43a53c5 into master Apr 8, 2026
4 checks passed
@intel352 intel352 deleted the copilot/fix-yaml-reformatting-issue branch April 8, 2026 15:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Editor destructively reformats YAML on file open (no user action)

3 participants